﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.Modeling.Validation;

namespace Microsoft.Samples.DslTools.ConfigurationSectionDesigner
{
    [ValidationState(ValidationState.Enabled)]
    public partial class ConfigurationElement
    {
        #region Convenience Properties

        /// <summary>
        /// Gets the value of the calculated TypeName property.
        /// </summary>
        /// <remarks>
        /// This property is used to show the actual type of the MEL in the shape,
        /// i.e. ConfigurationElement, ConfigurationElementCollection or ConfigurationSection.
        /// </remarks>
        public string GetTypeNameValue()
        {
            return this.GetType().Name;
        }

        /// <summary>
        /// Gets all configuration properties as one list (both Elements and Attributes).
        /// </summary>
        public IList<ConfigurationProperty> Properties
        {
            get
            {
                List<ConfigurationProperty> properties = new List<ConfigurationProperty>();
                foreach (ConfigurationProperty attributeProperty in this.AttributeProperties)
                {
                    properties.Add(attributeProperty);
                }
                foreach (ConfigurationProperty elementProperty in this.ElementProperties)
                {
                    properties.Add(elementProperty);
                }
                return properties;
            }
        }

        /// <summary>
        /// Gets a full documentation text based on the user-given <see cref="Documentation"/>.
        /// </summary>
        /// <remarks>
        /// If no user-given documentation is present, a "good enough" default description is returned.
        /// </remarks>
        public virtual string DocumentationText
        {
            get
            {
                return (string.IsNullOrEmpty(this.Documentation) ? string.Format("The {0} Configuration Element.", this.Name) : this.Documentation);
            }
        }

        #endregion

        #region NameChanged Event (for validation and for derived classes to set XML name)

        /// <summary>
        /// Occurs when the name of the ConfigurationElement changed.
        /// </summary>
        protected event EventHandler<EventArgs> NameChanged;

        /// <summary>
        /// Raises the <see cref="E:NameChanged"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnNameChanged(EventArgs e)
        {
            if (this.NameChanged != null)
            {
                this.NameChanged(this, EventArgs.Empty);
            }
        }

        internal sealed partial class NamePropertyHandler
        {
            protected override void OnValueChanged(ConfigurationElement element, string oldValue, string newValue)
            {
                if (!element.Store.InUndoRedoOrRollback)
                {
                    // Hard validation of the new name.
                    if (string.IsNullOrEmpty(newValue))
                    {
                        throw new ArgumentException("The Name is required and cannot be an empty string.", newValue);
                    }

                    // Raise the NameChanged event for derived classes to act upon.
                    element.OnNameChanged(EventArgs.Empty);
                }

                // Always call the base class method.
                base.OnValueChanged(element, oldValue, newValue);
            }
        }

        #endregion

        #region Validation

        /// <summary>
        /// Validates the Name property.
        /// </summary>
        /// <param name="context">The validation context.</param>
        [ValidationMethod(ValidationCategories.Menu | ValidationCategories.Open | ValidationCategories.Save)]
        private void ValidateName(ValidationContext context)
        {
            if (string.IsNullOrEmpty(this.Name))
            {
                context.LogError("The Name is required and cannot be an empty string.", "RequiredProperty", this);
            }
        }

        #endregion
    }
}